package cc.shacocloud.kotlin.tools.cli

import cc.shacocloud.kotlin.tools.SimpleScript
import cc.shacocloud.kotlin.tools.Slf4j
import kotlinx.coroutines.runBlocking
import java.io.File
import java.net.URLClassLoader
import kotlin.script.experimental.api.*
import kotlin.script.experimental.dependencies.CompoundDependenciesResolver
import kotlin.script.experimental.dependencies.DependsOn
import kotlin.script.experimental.dependencies.Repository
import kotlin.script.experimental.dependencies.resolveFromScriptSourceAnnotations
import kotlin.script.experimental.host.toScriptSource
import kotlin.script.experimental.jvm.*
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate

/**
 * 运行 kotlin 脚本
 * @author 思追(shaco)
 */
@Slf4j
object RunKotlinScript {

    // 隔离类加载器
    private val isolationClassLoader = IsolationClassLoader(
        classLoader = javaClass.classLoader,
        whitelist = setOf(
            //  jdk 相关
            "java.",
            "jdk.",
            "sun.",
            "javax.",
            "com.sun.",
            "org.ietf.jgss.",
            "org.w3c.dom.",
            "org.xml.sax.",
            "org.jcp.xml.dsig.internal.",
            "netscape.javascript.",
            // 日志相关
            "org.slf4j.",
            "ch.qos.logback."
        ),
        blacklist = emptySet()
    )

    /**
     * 执行脚本文件
     * @param scriptFile 脚本文件
     * @param libs 额外的依赖地址
     */
    fun evalFile(
        scriptFile: File,
        libs: List<File> = emptyList(),
    ): ResultWithDiagnostics<EvaluationResult> {

        // 运行时类加载器
        val classLoader =
            URLClassLoader(
                "KotlinScriptURLClassLoader",
                libs.map { it.toURI().toURL() }.toTypedArray(),
                isolationClassLoader
            )

        return classLoader.use {

            // 编译配置
            val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<SimpleScript> {

                defaultImports(DependsOn::class, Repository::class)

                jvm {
                    jvmTarget.put("21")
                    dependencies.append(JvmDependency(libs))
                }

                refineConfiguration {
                    onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
                }
            }

            // 执行配置
            val evaluationConfiguration = createJvmEvaluationConfigurationFromTemplate<SimpleScript> {
                jvm {

                    set(
                        ScriptEvaluationConfiguration.jvm.baseClassLoader,
                        classLoader
                    )

                    set(
                        ScriptEvaluationConfiguration.jvm.lastSnippetClassLoader,
                        classLoader
                    )
                }
            }

            BasicJvmScriptingHost()
                .eval(
                    scriptFile.toScriptSource(),
                    compilationConfiguration,
                    evaluationConfiguration
                )
        }
    }

    /**
     * 依赖解析器
     */
    private val dependenciesResolver = CompoundDependenciesResolver(MavenDependenciesResolver(MavenRepository))

    /**
     * 基于注解配置 maven 依赖
     */
    private fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
        val annotations =
            context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
                ?: return context.compilationConfiguration.asSuccess()

        return runBlocking {
            dependenciesResolver.resolveFromScriptSourceAnnotations(annotations)
        }.onSuccess {
            context.compilationConfiguration.with { dependencies.append(JvmDependency(it)) }.asSuccess()
        }
    }
}
